/**
* @file mtlloader.cpp
 * @brief MtlLoader class dimplementation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 3 of the License.
 *
 * @author VestniK (Sergey N.Vidyuk) sir.vestnik@gmail.com
 * @date 23 Aug 2010
 */
#include "mtlloader.h"
#include "mtlloaderprivate.h"

#include <QtCore/QFile>
#include <QtCore/QTextStream>

#include "mtlmaterial.h"

using namespace mjn;

MtlMaterial::MtlMaterial()
{
    for (int i = 0; i < REFLECTIVITY_SIZE; i++) {
        ambientReflectivity[i] = 0.0;
        diffuseReflectivity[i] = 0.0;
        specularReflectivity[i] = 0.0;
    }
    illuminationModel = 0;
    alpha = 1.0;
    specularExponent = 0.0;
    opticalDensity = 1.0;
}

MtlLoader::MtlLoader():
    d(new MtlLoaderPrivate)
{
    mValid = false;
    d->error = tr("Not initialized");
    d->errorLine = 0;
}

MtlLoader::MtlLoader(const QString &path):
    d(new MtlLoaderPrivate)
{
    load(path);
}

MtlLoader::~MtlLoader()
{
    delete d;
}

bool MtlLoader::load(const QString &path)
{
    mValid = false;
    QFile file(path);
    if (!file.exists()) {
        d->error = tr("File '%1' doesn't exists").arg(path);
        return false;
    }
    if (!file.open(QIODevice::ReadOnly)) {
        d->error = tr("Can't open '%1' for reading").arg(path);
        return false;
    }
    QTextStream stream(&file);

    while (!stream.atEnd()) {
        QString line = stream.readLine();
        d->errorLine++;
        line = line.trimmed();
        if (line.isEmpty() || line.startsWith('#')) continue;

        if (!d->loadDefinition(line)) return false;
    }

    mValid = true;
    d->errorLine = 0;
    return true;
}

const QString &MtlLoader::errorString() const
{
    return d->error;
}

int MtlLoader::errorLine() const
{
    return d->errorLine;
}

bool MtlLoaderPrivate::loadDefinition(const QString &definition)
{
    QStringList tokens = definition.split(QRegExp("\\s+"));
    if (tokens[0] == "newmtl") {
        if (tokens.size() != 2) {
            error = MtlLoader::tr("newmtl statement should have one argument specifying material name!");
            return false;
        }
        currentMaterial = tokens[1];
        materials[currentMaterial] = MtlMaterial();
        return true;
    }
    if (currentMaterial.isEmpty()) {
        error = MtlLoader::tr("No material is specified. Any definitions should be preceeded by "
                              "'newmtl material_name' statement!");
        return false;
    }
    if (tokens[0].startsWith('K')) {
        return loadReflectivityElement(tokens);
    } else if (tokens[0] == "d" || tokens[0] == "Ts") {
        return loadAlpha(tokens);
    } else if (tokens[0] == "Ns") {
        qDebug("loading of material shiness have not yet been implemented.");
        return true;
    } else if (tokens[0] == "Ni") {
        qDebug("loading of material optical density have not yet been implemented.");
        return true;
    } else if (tokens[0] == "illum") {
        qDebug("loading of material illumination model have not yet been implemented.");
        return true;
    }
    error = MtlLoader::tr("Unknown statement: %1").arg(tokens[0]);
    return false;
}

bool MtlLoaderPrivate::loadReflectivityElement(const QStringList &tokens)
{
    double *target;
    if (tokens[0] == "Ka") {
        target = materials[currentMaterial].ambientReflectivity;
    } else if (tokens[0] == "Kd") {
        target = materials[currentMaterial].diffuseReflectivity;
    } else if (tokens[0] == "Ks") {
        target = materials[currentMaterial].specularReflectivity;
    } else {
        error = MtlLoader::tr("Unknown statement %1").arg(tokens[0]);
    }
    if (tokens.size() != REFLECTIVITY_SIZE+1) {
        error = MtlLoader::tr("Wrong number of arguments in color specification. "
                              "Should be '%1 r g b'.").arg(tokens[0]);
        return false;
    }
    bool ok;
    for (int i = 0; i < REFLECTIVITY_SIZE; i++) {
        target[i] = tokens[i+1].toDouble(&ok);
        if (!ok) {
            error = MtlLoader::tr("Wrong format of color specification. "
                                  "Should be '%1 r g b' where 'r', 'g' and 'b' "
                                  "are float numbers.").arg(tokens[0]);
            return false;
        }
    }
    return true;
}

bool MtlLoaderPrivate::loadAlpha(const QStringList &tokens)
{
    if (tokens.size() != 2) {
        error = MtlLoader::tr("Wrong format. Statement '%1 alpha' "
                              "requires one float parametr").arg(tokens[0]);
        return false;
    }
    bool ok;
    materials[currentMaterial].alpha = tokens[1].toDouble(&ok);
    if (!ok) {
        error = MtlLoader::tr("Wrong format. Statement '%1 alpha' "
                              "requires one float parametr").arg(tokens[0]);
        return false;
    }
    return true;
}
